/*
 * ComputeSurface.cpp
 *
 *  Created on: 8 Nov 2013
 *      Author: Patrick
 */

#include <OgreColourValue.h>
#include <OgreVector3.h>
#include <OgreManualObject.h>
#include <OgreSceneManager.h>
#include <OgreImage.h>

#include <engine/algorithms/ComputeSurface.h>

namespace Engine {

HermiteSurface* ComputeSurface::computeSurface(
			Ogre::Image* img,
			float maxHeight) {

	int arrayWidth = img->getWidth();
	int arrayHeight = img->getHeight();

	/*
	 * create and initialise height-data
	 */
	std::vector<std::vector< float> > data;
	data.resize(arrayWidth);
	for(int i = 0; i < arrayWidth; i++) {
		data[i].resize(arrayHeight);
		for(int j = 0; j < arrayHeight; j++) {
			Ogre::ColourValue c = img->getColourAt(i, j, 0);
			float grayscale = (0.2126 * c.r + 0.7152 * c.g + 0.0722 * c.b) * maxHeight;
			data[i][j] = grayscale;
		}
	}//end of create and initialise data

	/*
	 * create and initialise splines
	 */
	HermiteSurface* pointArray = new HermiteSurface(arrayWidth, arrayHeight);
	for(int i = 0; i < arrayWidth; i++) {
		for(int j = 0; j < arrayHeight; j++) {
			Ogre::Vector3 p = Ogre::Vector3(i, j, data[i][j]);
			pointArray->setPosition(i, j, p);
		}
	}

	//create vertical tangents
	for(int i = 0; i < arrayWidth; i++) {

		//start
		Ogre::Vector3 firstpoint;
		pointArray->getPosition(i, 0, firstpoint);
		Ogre::Vector3 secondpoint;
		pointArray->getPosition(i, 1, secondpoint);
		Ogre::Vector3 start = secondpoint - firstpoint;
		pointArray->setVerticalTangent(i, 0, start);

		//end
		Ogre::Vector3 lastpoint;
		pointArray->getPosition(i, arrayHeight-1, lastpoint);
		Ogre::Vector3 secondlastpoint;
		pointArray->getPosition(i, arrayHeight-2, secondlastpoint);
		Ogre::Vector3 end = lastpoint - secondlastpoint;
		pointArray->setVerticalTangent(i, arrayHeight-1, end);

		//the rest
		for(int j = 1; j < arrayHeight - 1; j++) {
			Ogre::Vector3 p0;
			pointArray->getPosition(i, j-1, p0);
			Ogre::Vector3 p1;
			pointArray->getPosition(i, j+1, p1);
			Ogre::Vector3 t = (p1 - p0) * 0.5;
			pointArray->setVerticalTangent(i, j, t);
		}
	}//end of create vertical tangents

	//create horizontal tangents
	for(int j = 0; j < arrayHeight; j++) {

		//start
		Ogre::Vector3 firstpoint;
		pointArray->getPosition(0, j, firstpoint);
		Ogre::Vector3 secondpoint;
		pointArray->getPosition(1, j, secondpoint);
		Ogre::Vector3 start = secondpoint - firstpoint;
		pointArray->setHorizontalTangent(0, j, start);

		//end
		Ogre::Vector3 lastpoint;
		pointArray->getPosition(arrayWidth-1, j, lastpoint);
		Ogre::Vector3 secondlastpoint;
		pointArray->getPosition(arrayWidth-2, j, secondlastpoint);
		Ogre::Vector3 end = lastpoint - secondlastpoint;
		pointArray->setHorizontalTangent(arrayWidth-1, j, end);

		//the rest
		for(int i = 1; i < arrayWidth - 1; i++) {
			Ogre::Vector3 p0;
			pointArray->getPosition(i-1, j, p0);
			Ogre::Vector3 p1;
			pointArray->getPosition(i+1, j, p1);
			Ogre::Vector3 t = (p1 - p0) * 0.5;
			pointArray->setHorizontalTangent(i, j, t);
		}
	}//end of create horizontal tangents

	return pointArray;
}

//=========================================================================================

Ogre::ManualObject* ComputeSurface::renderSurface(
		HermiteSurface* points,
		Ogre::SceneManager* sceneMgr,
		Ogre::ColourValue colour) {

	Ogre::ManualObject* hermiteSurface = sceneMgr->createManualObject();
	int arrayWidth = points->getXsize();
	int arrayHeight = points->getYsize();

	//render vertical splines
	for(int i = 0; i < arrayWidth; i++) {
		hermiteSurface->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP);

		for(int j = 0; j < arrayHeight - 1; j++) {
			Ogre::Vector3 p0;
			points->getPosition(i, j, p0);
			Ogre::Vector3 p1;
			points->getPosition(i, j+1, p1);
			Ogre::Vector3 t0;
			points->getVerticalTangent(i, j, t0);
			Ogre::Vector3 t1;
			points->getVerticalTangent(i, j+1, t1);

			for(float t = 0.1f; t < 1.0f; t += 0.1f) {
				Ogre::Vector3 p = (2.0*t*t*t - 3.0*t*t + 1.0) * p0
						+ (t*t*t - 2.0*t*t + t) * t0
						+ (-2.0*t*t*t + 3.0*t*t) * p1
						+ (t*t*t - t*t) * t1;

				hermiteSurface->position(p);
				hermiteSurface->colour(colour);
			}
		}

		hermiteSurface->end();
	}//end of render vertical splines

	//render horizontal splines
	for(int j = 0; j < arrayHeight; j++) {
		hermiteSurface->begin("BaseWhiteNoLighting", Ogre::RenderOperation::OT_LINE_STRIP);

		for(int i = 0; i < arrayWidth - 1; i++) {
			Ogre::Vector3 p0;
			points->getPosition(i, j, p0);
			Ogre::Vector3 p1;
			points->getPosition(i+1, j, p1);
			Ogre::Vector3 t0;
			points->getHorizontalTangent(i, j, t0);
			Ogre::Vector3 t1;
			points->getHorizontalTangent(i+1, j, t1);

			for(float t = 0.1f; t < 1.0f; t += 0.1f) {
				Ogre::Vector3 p = (2.0*t*t*t - 3.0*t*t + 1.0) * p0
						+ (t*t*t - 2.0*t*t + t) * t0
						+ (-2.0*t*t*t + 3.0*t*t) * p1
						+ (t*t*t - t*t) * t1;

				hermiteSurface->position(p);
				hermiteSurface->colour(colour);
			}
		}

		hermiteSurface->end();
	}//end of render horizontal splines

	return hermiteSurface;
}

} /* namespace Engine */
